/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.loader;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.ibatis.executor.ExecutorException;

import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.property.PropertyCopier;
import org.apache.ibatis.reflection.property.PropertyNamer;

/**
 * 增强反序列代理
 * 
 * @author Clinton Begin
 */
public abstract class AbstractEnhancedDeserializationProxy {

	protected static final String FINALIZE_METHOD = "finalize";
	protected static final String WRITE_REPLACE_METHOD = "writeReplace";
	/**
	 * 原生的对象类型
	 */
	private Class<?> type;
	/**
	 * 不加载的属性
	 */
	private Map<String, ResultLoaderMap.LoadPair> unloadedProperties;
	/**
	 * 对象工厂
	 */
	private ObjectFactory objectFactory;
	/**
	 * 构造函数的参数类型
	 */
	private List<Class<?>> constructorArgTypes;
	/**
	 * 构造函数的参数值
	 */
	private List<Object> constructorArgs;
	/**
	 * 重加载属性锁对象
	 */
	private final Object reloadingPropertyLock;
	/**
	 * 是否存在重加载属性
	 */
	private boolean reloadingProperty;

	/**
	 * 初始化
	 * 
	 * @param type
	 *            原生的对象类型
	 * @param unloadedProperties
	 *            不加载的属性
	 * @param objectFactory
	 *            对象工厂
	 * @param constructorArgTypes
	 *            构造函数的参数类型
	 * @param constructorArgs
	 *            构造函数的参数值
	 */
	protected AbstractEnhancedDeserializationProxy(Class<?> type,
			Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
			List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
		this.type = type;
		this.unloadedProperties = unloadedProperties;
		this.objectFactory = objectFactory;
		this.constructorArgTypes = constructorArgTypes;
		this.constructorArgs = constructorArgs;
		this.reloadingPropertyLock = new Object();
		this.reloadingProperty = false;
	}

	/**
	 * 反射
	 * 
	 * @param enhanced
	 * @param method
	 * @param args
	 * @return
	 * @throws Throwable
	 */
	public final Object invoke(Object enhanced, Method method, Object[] args) throws Throwable {
		final String methodName = method.getName();
		try {
			// 判断方法名是否等于writeReplace
			if (WRITE_REPLACE_METHOD.equals(methodName)) {
				final Object original;
				// 如果构造函数没有参数，创建代理对象
				if (constructorArgTypes.isEmpty()) {
					original = objectFactory.create(type);
				} else {
					// 否则，初始化带有参数的构造函数
					original = objectFactory.create(type, constructorArgTypes, constructorArgs);
				}
				// enhanced是type的初始化对象
				// original可能是type的代理对象
				// 将enhanced对象的值赋值到original，包括父类的字段赋值
				PropertyCopier.copyBeanProperties(type, enhanced, original);
				return this.newSerialStateHolder(original, unloadedProperties, objectFactory, constructorArgTypes,
						constructorArgs);
			} else {
				// 同步
				synchronized (this.reloadingPropertyLock) {
					// 判断方法名不等于finalize，并且方法名是属性，同时存在重加载属性
					if (!FINALIZE_METHOD.equals(methodName) && PropertyNamer.isProperty(methodName)
							&& !reloadingProperty) {
						final String property = PropertyNamer.methodToProperty(methodName);
						final String propertyKey = property.toUpperCase(Locale.ENGLISH);
						// 判断当前属性是否在不加载的属性集里
						if (unloadedProperties.containsKey(propertyKey)) {
							// 移除这个属性
							final ResultLoaderMap.LoadPair loadPair = unloadedProperties.remove(propertyKey);
							// 如果这个属性还存在
							if (loadPair != null) {
								try {
									// 是否存在重加载属性
									reloadingProperty = true;
									loadPair.load(enhanced);
								} finally {
									reloadingProperty = false;
								}
							} else {
								/*
								 * I'm not sure if this case can really happen
								 * or is just in tests - we have an unread
								 * property but no loadPair to load it.
								 */
								throw new ExecutorException(
										"An attempt has been made to read a not loaded lazy property '" + property
												+ "' of a disconnected object");
							}
						}
					}

					return enhanced;
				}
			}
		} catch (Throwable t) {
			throw ExceptionUtil.unwrapThrowable(t);
		}
	}

	/**
	 * 创建一个AbstractSerialStateHolder对象
	 * 
	 * @param userBean
	 *            使用的bean
	 * @param unloadedProperties
	 *            不加载的属性集
	 * @param objectFactory
	 *            对象工厂
	 * @param constructorArgTypes
	 *            构造函数的参数类型
	 * @param constructorArgs
	 *            构造函数的参数值
	 * @return
	 */
	protected abstract AbstractSerialStateHolder newSerialStateHolder(Object userBean,
			Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
			List<Class<?>> constructorArgTypes, List<Object> constructorArgs);

}
